package com.dib.neo.bkfs.fs;

import java.io.IOException;
import java.net.URI;
import java.nio.file.*;
import java.nio.file.attribute.UserPrincipalLookupService;
import java.nio.file.spi.FileSystemProvider;
import java.util.*;

public class BKFileSystem extends FileSystem {

  private final BKFileSystemProvider provider;
  private final BKPath rootDirectory;
  private final byte[] defaultDirectory;
  private final String connString;
  private final BKFSClient bkfs;

  public BKFileSystem(BKFileSystemProvider provider, String connStr, String dir) {
    this.provider = provider;

    this.connString = connStr;
    defaultDirectory = dir.getBytes();
    if (this.defaultDirectory[0] != '/') {
      throw new RuntimeException("default directory must be absolute");
    }

    try {
      bkfs = new BKFSClient(connString);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
    rootDirectory = new BKPath(this, "/");
  }

  BKFSClient bkfs() {
    return bkfs;
  }

  FileStore getFileStore() {
    return new BKFileStore(this);
  }

  byte[] defaultDirectory() {
    return defaultDirectory;
  }

  public String getConnString() {
    return connString;
  }

  @Override
  public FileSystemProvider provider() {
    return this.provider;
  }

  BKPath rootDirectory() {
    return rootDirectory;
  }

  @Override
  public void close() throws IOException {
    this.provider.free(this);
    bkfs.close();
  }

  @Override
  public boolean isOpen() {
    return true;
  }

  @Override
  public boolean isReadOnly() {
    return false;
  }

  @Override
  public String getSeparator() {
    return "/";
  }

  @Override
  public Iterable<Path> getRootDirectories() {
    final List<Path> allowedList =
        Collections.unmodifiableList(Arrays.asList((Path) rootDirectory));
    return new Iterable<Path>() {
      public Iterator<Path> iterator() {
        try {
          SecurityManager sm = System.getSecurityManager();
          if (sm != null) {
            sm.checkRead(rootDirectory.toString());
          }
          return allowedList.iterator();
        } catch (SecurityException x) {
          List<Path> disallowed = Collections.emptyList();
          return disallowed.iterator();
        }
      }
    };
  }

  @Override
  public Iterable<FileStore> getFileStores() {
    return null;
  }

  @Override
  public Set<String> supportedFileAttributeViews() {
    return new HashSet<>(Arrays.asList("basic", "posix"));
  }

  @Override
  public Path getPath(String first, String... more) {
    String path;
    if (more.length == 0) {
      path = first;
    } else {
      StringBuilder sb = new StringBuilder();
      sb.append(first);
      for (String segment : more) {
        if (segment.length() > 0) {
          if (sb.length() > 0) {
            sb.append('/');
          }
          sb.append(segment);
        }
      }
      path = sb.toString();
    }
    return new BKPath(this, path);
  }

  @Override
  public PathMatcher getPathMatcher(String syntaxAndPattern) {
    throw new UnsupportedOperationException();
  }

  @Override
  public UserPrincipalLookupService getUserPrincipalLookupService() {
    throw new UnsupportedOperationException();
  }

  @Override
  public WatchService newWatchService() throws IOException {
    throw new UnsupportedOperationException();
  }

  public URI toUri(String path) {
    StringBuilder s = new StringBuilder(this.toString());
    s.append(path);
    return URI.create(s.toString());
  }

  @Override
  public String toString() {
    StringBuilder s = new StringBuilder(BKFileSystemProvider.BKFILE_SCEME);
    s.append("://").append(connString);
    return s.toString();
  }
}
